home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Gold Collection
/
Software Vault - The Gold Collection (American Databankers) (1993).ISO
/
cdr26
/
netprog.zip
/
NETPROG.TAR
/
tftp
/
sendrecv.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-12-17
|
13KB
|
515 lines
/*
* Send and receive packets.
*/
#include "defs.h"
#include <sys/stat.h>
#include <ctype.h>
#ifdef CLIENT
/*
* Send a Read-Request or a Write-Request to the other system.
* These two packets are only sent by the client to the server.
* This function is called when either the "get" command or the
* "put" command is executed by the user.
*/
send_RQ(opcode, fname, mode)
int opcode; /* OP_RRQ or OP_WRQ */
char *fname;
int mode;
{
register int len;
char *modestr;
DEBUG2("sending RRQ/WRQ for %s, mode = %d", fname, mode);
stshort(opcode, sendbuff);
strcpy(sendbuff+2, fname);
len = 2 + strlen(fname) + 1; /* +1 for null byte at end of fname */
switch(mode) {
case MODE_ASCII: modestr = "netascii"; break;
case MODE_BINARY: modestr = "octet"; break;
default:
err_dump("unknown mode");
}
strcpy(sendbuff + len, modestr);
len += strlen(modestr) + 1; /* +1 for null byte at end of modestr */
sendlen = len;
net_send(sendbuff, sendlen);
op_sent = opcode;
}
/*
* Error packet received in response to an RRQ or a WRQ.
* Usually means the file we're asking for on the other system
* can't be accessed for some reason. We need to print the
* error message that's returned.
* Called by finite state machine.
*/
int
recv_RQERR(ptr, nbytes)
char *ptr; /* points just past received opcode */
int nbytes; /* doesn't include received opcode */
{
register int ecode;
ecode = ldshort(ptr);
ptr += 2;
nbytes -= 2;
ptr[nbytes] = 0; /* assure it's null terminated ... */
DEBUG2("ERROR received, %d bytes, error code %d", nbytes, ecode);
fflush(stdout);
fprintf(stderr, "Error# %d: %s\n", ecode, ptr);
fflush(stderr);
return(-1); /* terminate finite state loop */
}
#endif /* CLIENT */
/*
* Send an acknowledgment packet to the other system.
* Called by the recv_DATA() function below and also called by
* recv_WRQ().
*/
send_ACK(blocknum)
int blocknum;
{
DEBUG1("sending ACK for block# %d", blocknum);
stshort(OP_ACK, sendbuff);
stshort(blocknum, sendbuff + 2);
sendlen = 4;
net_send(sendbuff, sendlen);
#ifdef SORCERER
/*
* If you want to see the Sorcerer's Apprentice syndrome,
* #define SORCERER, then run this program as the client and
* get a file from a server that doesn't have the bug fixed
* (such as the 4.3BSD version).
* Turn on the trace option, and you'll see the duplicate
* data packets sent by the broken server, starting with
* block# 2. Yet when the transfer is complete, you'll find
* the file was received correctly.
*/
if (blocknum == 1)
net_send(sendbuff, sendlen); /* send the first ACK twice */
#endif
op_sent = OP_ACK;
}
/*
* Send data to the other system.
* The data must be stored in the "sendbuff" by the caller.
* Called by the recv_ACK() function below.
*/
send_DATA(blocknum, nbytes)
int blocknum;
int nbytes; /* #bytes of actual data to send */
{
DEBUG2("sending %d bytes of DATA with block# %d", nbytes, blocknum);
stshort(OP_DATA, sendbuff);
stshort(blocknum, sendbuff + 2);
sendlen = nbytes + 4;
net_send(sendbuff, sendlen);
op_sent = OP_DATA;
}
/*
* Data packet received. Send an acknowledgment.
* Called by finite state machine.
* Note that this function is called for both the client and the server.
*/
int
recv_DATA(ptr, nbytes)
register char *ptr; /* points just past received opcode */
register int nbytes; /* doesn't include received opcode */
{
register int recvblknum;
recvblknum = ldshort(ptr);
ptr += 2;
nbytes -= 2;
DEBUG2("DATA received, %d bytes, block# %d", nbytes, recvblknum);
if (nbytes > MAXDATA)
err_dump("data packet received with length = %d bytes", nbytes);
if (recvblknum == nextblknum) {
/*
* The data packet is the expected one.
* Increment our expected-block# for the next packet.
*/
nextblknum++;
totnbytes += nbytes;
if (nbytes > 0) {
/*
* Note that the final data packet can have a
* data length of zero, so we only write the
* data to the local file if there is data.
*/
file_write(localfp, ptr, nbytes, modetype);
}
#ifdef SERVER
/*
* If the length of the data is between 0-511, this is
* the last data block. For the server, here's where
* we have to close the file. For the client, the
* "get" command processing will close the file.
*/
if (nbytes < MAXDATA)
file_close(localfp);
#endif
} else if (recvblknum < (nextblknum - 1)) {
/*
* We've just received data block# N (or earlier, such as N-1,
* N-2, etc.) from the other end, but we were expecting data
* block# N+2. But if we were expecting N+2 it means we've
* already received N+1, so the other end went backwards from
* N+1 to N (or earlier). Something is wrong.
*/
err_dump("recvblknum < nextblknum - 1");
} else if (recvblknum > nextblknum) {
/*
* We've just received data block# N (or later, such as N+1,
* N+2, etc.) from the other end, but we were expecting data
* block# N-1. But this implies that the other end has
* received an ACK for block# N-1 from us. Something is wrong.
*/
err_dump("recvblknum > nextblknum");
}
/*
* The only case not handled above is "recvblknum == (nextblknum - 1)".
* This means the other end never saw our ACK for the last data
* packet and retransmitted it. We just ignore the retransmission
* and send another ACK.
*
* Acknowledge the data packet.
*/
send_ACK(recvblknum);
/*
* If the length of the data is between 0-511, we've just
* received the final data packet, else there is more to come.
*/
return( (nbytes == MAXDATA) ? 0 : -1 );
}
/*
* ACK packet received. Send some more data.
* Called by finite state machine. Also called by recv_RRQ() to
* start the transmission of a file to the client.
* Note that this function is called for both the client and the server.
*/
int
recv_ACK(ptr, nbytes)
register char *ptr; /* points just past received opcode */
register int nbytes; /* doesn't include received opcode */
{
register int recvblknum;
recvblknum = ldshort(ptr);
if (nbytes != 2)
err_dump("ACK packet received with length = %d bytes",
nbytes + 2);
DEBUG1("ACK received, block# %d", recvblknum);
if (recvblknum == nextblknum) {
/*
* The received acknowledgment is for the expected data
* packet that we sent.
* Fill the transmit buffer with the next block of data
* to send.
* If there's no more data to send, then we might be
* finished. Note that we must send a final data packet
* containing 0-511 bytes of data. If the length of the
* last packet that we sent was exactly 512 bytes, then we
* must send a 0-length data packet.
*/
if ( (nbytes = file_read(localfp, sendbuff + 4,
MAXDATA, modetype)) == 0) {
if (lastsend < MAXDATA)
return(-1); /* done */
/* else we'll send nbytes=0 of data */
}
lastsend = nbytes;
nextblknum++; /* incr for this new packet of data */
totnbytes += nbytes;
send_DATA(nextblknum, nbytes);
return(0);
} else if (recvblknum < (nextblknum - 1)) {
/*
* We've just received the ACK for block# N (or earlier, such
* as N-1, N-2, etc) from the other end, but we were expecting
* the ACK for block# N+2. But if we're expecting the ACK for
* N+2 it means we've already received the ACK for N+1, so the
* other end went backwards from N+1 to N (or earlier).
* Something is wrong.
*/
err_dump("recvblknum < nextblknum - 1");
} else if (recvblknum > nextblknum) {
/*
* We've just received the ACK for block# N (or later, such
* as N+1, N+2, etc) from the other end, but we were expecting
* the ACK for block# N-1. But this implies that the other
* end has already received data block# N-1 from us.
* Something is wrong.
*/
err_dump("recvblknum > nextblknum");
} else {
/*
* Here we have "recvblknum == (nextblknum - 1)".
* This means we received a duplicate ACK. This means either:
* (1) the other side never received our last data packet;
* (2) the other side's ACK got delayed somehow.
*
* If we were to retransmit the last data packet, we would start
* the "Sorcerer's Apprentice Syndrome." We'll just ignore this
* duplicate ACK, returning to the FSM loop, which will initiate
* another receive.
*/
return(0);
}
/* NOTREACHED */
}
#ifdef SERVER
/*
* RRQ packet received.
* Called by the finite state machine.
* This (and receiving a WRQ) are the only ways the server gets started.
*/
int
recv_RRQ(ptr, nbytes)
char *ptr;
int nbytes;
{
char ackbuff[2];
recv_xRQ(OP_RRQ, ptr, nbytes); /* verify the RRQ packet */
/*
* Set things up so we can just call recv_ACK() and pretend we
* received an ACK, so it'll send the first data block to the
* client.
*/
lastsend = MAXDATA;
stshort(0, ackbuff); /* pretend its an ACK of block# 0 */
recv_ACK(ackbuff, 2); /* this sends data block# 1 */
return(0); /* the finite state machine takes over from here */
}
/*
* WRQ packet received.
* Called by the finite state machine.
* This (and receiving an RRQ) are the only ways the server gets started.
*/
int
recv_WRQ(ptr, nbytes)
char *ptr;
int nbytes;
{
recv_xRQ(OP_WRQ, ptr, nbytes); /* verify the WRQ packet */
/*
* Call send_ACK() to acknowledge block# 0, which will cause
* the client to send data block# 1.
*/
nextblknum = 1;
send_ACK(0);
return(0); /* the finite stat machine takes over from here */
}
/*
* Process an RRQ or WRQ that has been received.
* Called by the 2 routines above.
*/
int
recv_xRQ(opcode, ptr, nbytes)
int opcode; /* OP_RRQ or OP_WRQ */
register char *ptr; /* points just past received opcode */
register int nbytes; /* doesn't include received opcode */
{
register int i;
register char *saveptr;
char filename[MAXFILENAME], dirname[MAXFILENAME],
mode[MAXFILENAME];
struct stat statbuff;
/*
* Assure the filename and mode are present and
* null-terminated.
*/
saveptr = ptr; /* points to beginning of filename */
for (i = 0; i < nbytes; i++)
if (*ptr++ == '\0')
goto FileOK;
err_dump("Invalid filename");
FileOK:
strcpy(filename, saveptr);
saveptr = ptr; /* points to beginning of Mode */
for ( ; i < nbytes; i++)
if (*ptr++ == '\0')
goto ModeOK;
err_dump("Invalid Mode");
ModeOK:
strlccpy(mode, saveptr); /* copy and convert to lower case */
if (strcmp(mode, "netascii") == 0)
modetype = MODE_ASCII;
else if (strcmp(mode, "octet") == 0)
modetype = MODE_BINARY;
else
send_ERROR(ERR_BADOP, "Mode isn't netascii or octet");
/*
* Validate the filename.
* Note that as a daemon we might be running with root
* privileges. Since there are no user-access checks with
* tftp (as compared to ftp, for example) we will only
* allow access to files that are publicly accessible.
*
* Also, since we're running as a daemon, our home directory
* is the root, so any filename must have it's full
* pathname specified (i.e., it must begin with a slash).
*/
if (filename[0] != '/')
send_ERROR(ERR_ACCESS, "filename must begin with '/'");
if (opcode == OP_RRQ) {
/*
* Read request - verify that the file exists
* and that it has world read permission.
*/
if (stat(filename, &statbuff) < 0)
send_ERROR(ERR_ACCESS, sys_err_str());
if ((statbuff.st_mode & (S_IREAD >> 6)) == 0)
send_ERROR(ERR_ACCESS,
"File doesn't allow world read permission");
} else if (opcode == OP_WRQ) {
/*
* Write request - verify that the directory
* that the file is being written to has world
* write permission. We've already verified above
* that the filename starts with a '/'.
*/
char *rindex();
strcpy(dirname, filename);
*(rindex(dirname, '/') + 1) = '\0';
if (stat(dirname, &statbuff) < 0)
send_ERROR(ERR_ACCESS, sys_err_str());
if ((statbuff.st_mode & (S_IWRITE >> 6)) == 0)
send_ERROR(ERR_ACCESS,
"Directory doesn't allow world write permission");
} else
err_dump("unknown opcode");
localfp = file_open(filename, (opcode == OP_RRQ) ? "r" : "w", 0);
if (localfp == NULL)
send_ERROR(ERR_NOFILE, sys_err_str()); /* doesn't return */
}
/*
* Send an error packet.
* Note that an error packet isn't retransmitted or acknowledged by
* the other end, so once we're done sending it, we can exit.
*/
send_ERROR(ecode, string)
int ecode; /* error code, ERR_xxx from defs.h */
char *string; /* some additional info */
/* can't be NULL, set to "" if empty */
{
DEBUG2("sending ERROR, code = %d, string = %s", ecode, string);
stshort(OP_ERROR, sendbuff);
stshort(ecode, sendbuff + 2);
strcpy(sendbuff + 4, string);
sendlen = 4 + strlen(sendbuff + 4) + 1; /* +1 for null at end */
net_send(sendbuff, sendlen);
net_close();
exit(0);
}
/*
* Copy a string and convert it to lower case in the process.
*/
strlccpy(dest, src)
register char *dest, *src;
{
register char c;
while ( (c = *src++) != '\0') {
if (isupper(c))
c = tolower(c);
*dest++ = c;
}
*dest = 0;
}
#endif /* SERVER */